﻿using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using Singer.BackgroundJob.RabbitMQ.Contracts;
using System.Collections.Concurrent;
using static Singer.BackgroundJob.RabbitMQ.Contracts.Contants;

namespace Singer.BackgroundJob.RabbitMQ;

/// <summary>
/// 后台任务消费者 上下文类
/// </summary>
public class ConsumerContext : IDisposable
{
    /// <summary>
    /// 后台任务参数 类型
    /// </summary>
    public Type ArgsType { get; init; }
    /// <summary>
    /// 任务执行器 类型
    /// </summary>
    public Type ExecutorType { get; init; }
    /// <summary>
    /// 任务执行器 特性
    /// </summary>
    public BackgroundJobExecutorAttribute ExecutorAttribute { get; init; }
    /// <summary>
    /// 消息消费者
    /// </summary>
    internal EventingBasicConsumer? Consumer { get; private set; }
    /// <summary>
    /// 当前消费者独享的信道
    /// </summary>
    internal IModel? Channel { get; private set; }

    /// <summary>
    /// 当前消费者同时消费多个消息时，每条消息对应的后台任务日志id - 任务执行器的任务取消令牌 字典
    /// </summary>
    internal ConcurrentDictionary<string, CancellationTokenSource> CurrentJobExecutorStopptingCtsDic = new ConcurrentDictionary<string, CancellationTokenSource>();

    /// <summary>
    /// 后台任务消费者 上下文类
    /// </summary>
    /// <param name="argsType">后台任务参数 类型</param>
    /// <param name="executorType">后台任务执行器 类型</param>
    /// <param name="executorAttribute">后台任务执行器特性</param>
    internal ConsumerContext(Type argsType, Type executorType, BackgroundJobExecutorAttribute executorAttribute)
    {
        ArgsType = argsType;
        ExecutorType = executorType;
        ExecutorAttribute = executorAttribute;
    }

    /// <summary>
    /// 初始化 消费者
    /// </summary>
    internal void InitConsumer(EventingBasicConsumer consumer, IModel channel)
    {
        Consumer = consumer;
        Channel = channel;
    }

    /// <summary>
    /// 当消费者处理一条后台任务消息时，记录当前后台任务日志id，任务执行器取消令牌
    /// </summary>
    /// <param name="logId">当前后台任务日志id</param>
    /// <param name="executorStoppingCts">任务执行器取消令牌</param>
    internal void AddJob(string logId, CancellationTokenSource executorStoppingCts)
    {
        CurrentJobExecutorStopptingCtsDic.TryAdd(logId, executorStoppingCts);
    }

    /// <summary>
    /// 消息处理完成时
    /// </summary>
    internal void RemoveJob(string logId)
    {
        if (CurrentJobExecutorStopptingCtsDic.TryRemove(logId, out var cts))
        {
            cts?.Dispose();
            cts = null;
        }
    }

    public void Dispose()
    {
        foreach (var key in CurrentJobExecutorStopptingCtsDic.Keys)
        {
            CurrentJobExecutorStopptingCtsDic[key]?.Dispose();
        }
        CurrentJobExecutorStopptingCtsDic.Clear();
        Channel?.Dispose();
    }

    #region Consts

    /// <summary>
    /// 消费者 标识
    /// </summary>
    public string ConsumerName => $"{CONSUMERNAME_PREFIX}{ArgsType.FullName?.ToLower() ?? throw new Exception("后台任务参数类型不支持")}";
    /// <summary>
    /// 队列名
    /// </summary>
    public string QueueName => $"{QUEUENAME_PREFIX}{ArgsType.FullName?.ToLower() ?? throw new Exception("后台任务参数类型不支持")}";
    /// <summary>
    /// 路由键
    /// </summary>
    public string RoutingKey => $"{ROUTINGKEY_PREFIX}{ArgsType.FullName?.ToLower() ?? throw new Exception("后台任务参数类型不支持")}";
    #endregion
}
